﻿using System;
using LinFu.IoC.Configuration;
using LinFu.IoC.Factories;
using LinFu.IoC.Interfaces;
using LinFu.IoC;

namespace CoreEx.ServiceModel.Implementation
{
    /// <summary>
    /// Inspects the service request to determine if the requested service 
    /// represents a service contract.
    /// </summary>    
    [Preprocessor]
    public class ServiceContractPreProcessor : IPreProcessor
    {        
        /// <summary>
        /// Inspects the service request to determine if the requested service 
        /// represents a service contract.
        /// </summary>
        /// <param name="request">Contains information about the service request.</param>
        public void Preprocess(IServiceRequest request)
        {
            if (request.ServiceType.IsServiceContract() && !HasLocalImplementation(request))
            {
                Uri uri = CreateUri(request);

                var serviceProxyFactory = request.Container.GetService<IClientServiceProxyFactory>();                
                request.ActualFactory =
                       new FunctorFactory(r => serviceProxyFactory.CreateServiceProxy(r.ServiceType,uri));
            }
        }

        private static Uri CreateUri(IServiceRequest request)
        {
            string serviceName = request.ServiceName;
            if (ServiceNameRepresentsValidUri(serviceName))
                return CreateEndpointUri(new Uri(serviceName), request.ServiceType);
            
            var uriProvider = request.Container.GetService<IUriProvider>();
            Uri uri;
            if (String.IsNullOrEmpty(request.ServiceName))
                uri = uriProvider.CreateUri(request.ServiceType);
            else
                uri = uriProvider.CreateUri(request.ServiceType, request.ServiceName);
            var endPointUri =  CreateEndpointUri(uri, request.ServiceType);
            ThrowIfInvalidUri(endPointUri.OriginalString);
            return endPointUri;
            
        }

        private static Uri CreateEndpointUri(Uri baseaddress, Type type)
        {
            if (baseaddress.IsValidEndpointAddress())
                return baseaddress;
            
            return baseaddress.Combine(string.Format("{0}.svc", type.FullName));
        }

        private static void ThrowIfInvalidUri(string serviceUri)
        {
            if (!Uri.IsWellFormedUriString(serviceUri, UriKind.Absolute))
                throw new UriFormatException(string.Format("The URL {0} not a well formed Url", serviceUri));
        }


        private static bool ServiceNameRepresentsValidUri(string serviceName)
        {
            return Uri.IsWellFormedUriString(serviceName, UriKind.Absolute);
        }


        private static bool HasLocalImplementation(IServiceRequest request)
        {
            return request.ProposedFactory != null;
        }
    }
}